package scenic;

import java.awt.Rectangle;

import scenic.geom.Affine2;
import scenic.geom.Double2;
import scenic.jni.Renderer;

/**
 * This brush paints a repeating pattern. The pattern is created
 * by repeating the given scene.
 */
public class Pattern extends Brush implements SceneParent {
	private double width;
	private double height;
	private SceneNode pattern;
	
	/**
	 * Creates a pattern with the given width and height.
	 * @param width the width of the pattern.
	 * @param height the height of the pattern.
	 */
	public Pattern(double width, double height) {
		this.width = width;
		this.height = height;
	}

	/**
	 * Gets the width of the pattern.
	 */
	public double getWidth() {
		return width;
	}

	/**
	 * Sets the width of the pattern.
	 */
	public void setWidth(double width) {
		this.width = width;
		changed();
	}

	/**
	 * Gets the height of the pattern.
	 */
	public double getHeight() {
		return height;
	}

	/**
	 * Sets the height of the pattern.
	 */
	public void setHeight(double height) {
		this.height = height;
		changed();
	}

	/**
	 * Gets the scene that defines the pattern.
	 */
	public SceneNode getPattern() {
		return pattern;
	}

	/**
	 * Sets the scene that defines the pattern.
	 */
	public void setPattern(SceneNode pattern) {
		pattern.close();
		this.pattern = pattern;
		pattern.parent = this;
		changed();
	}
	
	public void remove(SceneNode child) {
		if(child == pattern) {
			pattern = null;
			child.parent = null;
			changed();
		}
	}

	void draw(DrawContext context, Affine2 transform, Rectangle visibleArea) {
		Affine2 itm = transform.inverse();
		double xmin = Double.MAX_VALUE;
		double xmax = Double.MIN_VALUE;
		double ymin = Double.MAX_VALUE;
		double ymax = Double.MIN_VALUE;
		
		for(int y = 0; y < 2; y++) {
			for(int x = 0; x < 2; x++) {
				Double2 p = itm.multiply(new Double2(
						visibleArea.x + visibleArea.width * x,
						visibleArea.y + visibleArea.height * y));
			
				xmin = Math.min(xmin, p.x);
				xmax = Math.max(xmax, p.x);
				ymin = Math.min(ymin, p.y);
				ymax = Math.max(ymax, p.y);
			}
		}

		xmin = Math.floor(xmin / width);
		xmax = Math.ceil(xmax / width);
		ymin = Math.floor(ymin / height);
		ymax = Math.ceil(ymax / height);

		for(int row = (int)ymin; row <= ymax; row++) {
			for(int col = (int)xmin; col <= xmax; col++) {
				drawBlock(context, transform, row, col, visibleArea);
			}
		}
	}
	
	private Rectangle getBlockBounds(Affine2 transform, int row, int col) {
		double xmin = Double.MAX_VALUE;
		double xmax = Double.MIN_VALUE;
		double ymin = Double.MAX_VALUE;
		double ymax = Double.MIN_VALUE;
		
		for(int y = 0; y < 2; y++) {
			for(int x = 0; x < 2; x++) {
				Double2 p = transform.multiply(new Double2(
						(x + col) * width, (y + row) * height));
				
				xmin = Math.min(xmin, p.x);
				xmax = Math.max(xmax, p.x);
				ymin = Math.min(ymin, p.y);
				ymax = Math.max(ymax, p.y);
			}
		}
		
		return new Rectangle((int)Math.floor(xmin) - 2,
				(int)Math.floor(ymin) - 2,
				(int)(Math.ceil(xmax) - Math.floor(xmin)) + 4,
				(int)(Math.ceil(ymax) - Math.floor(ymin)) + 4);
	}
	
	private void drawBlock(DrawContext context, Affine2 transform, int row, int col, 
			Rectangle visibleArea) {
		double corners[] = {
				0, 0, 
				width, 0, 
				width, height, 
				0, height};
		Rectangle blockBounds = addSafeMargin(getBlockBounds(transform, row, col));
		Affine2 tm = transform.multiply(Affine2.translate(col * width, row * height));

		if(!blockBounds.intersects(visibleArea))
			return;
		// Draw pattern
		if(context.renderer.beginSurface(context.context, blockBounds, Renderer.SURFACE_TYPE_COLOR) == 0)
			return;
		pattern.draw(context, tm, visibleArea);
		
		// Draw clip rectangle
		context.renderer.beginSurface(context.context, blockBounds, Renderer.SURFACE_TYPE_COLOR);
		context.renderer.setTransform(context.context, transform);
		context.renderer.color(context.context, 1.0f, 1.0f, 1.0f, 1.0f);
		context.renderer.setPolygonAntialias(context.context, false);
		context.renderer.tessBegin(context.context);
		context.renderer.tessBeginContour(context.context);			
		
		for(int i = 0; i < 4; i++) {
			context.renderer.tessVertex(context.context, 
					col * width + corners[i * 2], 
					row * height + corners[i * 2 + 1]);			
		}
		
		context.renderer.tessEndContour(context.context);
		context.renderer.tessEnd(context.context);
		
		// Combine clip and pattern
		context.renderer.color(context.context, 1.0f, 1.0f, 1.0f, 1.0f);
		context.renderer.drawSurfaceAndClip(context.context);
	}
	
	protected void setVisibility(boolean state) {
		super.setVisibility(state);
		if(pattern != null)
			pattern.setVisibility(state);
	}

	public void childChanged(Rectangle area) {
		if(parent != null)
			parent.childChanged(null);
	}	
}
